`include "vsrc/ysyx_20220337_define.v"

module ysyx_20220337_MEMU (
    input            clk,

    //I Cache
    input   [63:0]   pc,
    output  [31:0]   instr,
    //D Cache
    input   [2:0]    RW_type,
    input            Mem_read,
    input            Mem_write,
    input   [63:0]   addr,
    input   [63:0]   wdata,
    output  [63:0]   Read_data
);

    reg [7:0] ram [0:524287];

    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/add-longlong-riscv64-nemu.hex",ram);
    // initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/add-riscv64-nemu.hex",ram);
    // initial $readmemh("/home/dqs/01_work/ysyx22040337/npc/resource/dummy-riscv64-nemu.hex",ram);
    initial $readmemh("/home/dqs/01_work/ysyx22040337/npc/resource/add-riscv64-nemu.hex",ram);
    
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/bit-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/bubble-sort-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/div-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/dummy-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/fact-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/fib-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/goldbach-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/hello-str-riscv64-nemu.hex",ram);//not success
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/if-else-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/leap-year-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/load-store-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/matrix-mul-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/max-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/min3-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/mov-c-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/movsx-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/mul-longlong-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/pascal-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/prime-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/quick-sort-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/recursion-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/select-sort-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/shift-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/shuixianhua-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/string-riscv64-nemu.hex",ram);//not success
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/sub-longlong-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/sum-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/switch-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/to-lower-case-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/unalign-riscv64-nemu.hex",ram);
    //initial $readmemh("/home/dqs/ysyx-workbench/npc/test_hex/wanshu-riscv64-nemu.hex",ram);


    wire [63:0] instr_addr = (pc - 64'h80000000);
    wire [63:0] rw_addr = (addr - 64'h80000000);

    assign instr =  {ram[instr_addr + 64'h3],ram[instr_addr + 64'h2],ram[instr_addr + 64'h1],ram[instr_addr]};

    
    wire [7:0] wmask;
    wire [7:0] temp_rdata1;
    wire [7:0] temp_rdata2;
    wire [7:0] temp_rdata3;
    wire [7:0] temp_rdata4;
    wire [7:0] temp_rdata5;
    wire [7:0] temp_rdata6;
    wire [7:0] temp_rdata7;
    wire [7:0] temp_rdata8;
    wire [63:0] temp_rdata;
    wire [63:0] s_byte_rdata;
    wire [63:0] u_byte_rdata;
    wire [63:0] byte_rdata;
    wire [63:0] s_half_rdata;
    wire [63:0] u_half_rdata;
    wire [63:0] half_rdata;
    wire [63:0] s_word_rdata;
    wire [63:0] u_word_rdata;
    wire [63:0] word_rdata;
    
    wire unsigned_read = RW_type[2];
    assign wmask = (RW_type[1:0] == `ysyx_20220337_RW_8bit) ? 8'b0000_0001 :
                   (RW_type[1:0] == `ysyx_20220337_RW_16bit) ? 8'b0000_0011 :
                   (RW_type[1:0] == `ysyx_20220337_RW_32bit) ? 8'b0000_1111 :
                   (RW_type[1:0] == `ysyx_20220337_RW_64bit) ? 8'b1111_1111 :
                   8'b0000_0000 ;
    
    always @(posedge clk) begin
        if(Mem_write) begin
            if(wmask[0]) ram[rw_addr] <= wdata[7:0];
            if(wmask[1]) ram[rw_addr + 64'h1] <= wdata[15:8];
            if(wmask[2]) ram[rw_addr + 64'h2] <= wdata[23:16];
            if(wmask[3]) ram[rw_addr + 64'h3] <= wdata[31:24];
            if(wmask[4]) ram[rw_addr + 64'h4] <= wdata[39:32];
            if(wmask[5]) ram[rw_addr + 64'h5] <= wdata[47:40];
            if(wmask[6]) ram[rw_addr + 64'h6] <= wdata[55:48];
            if(wmask[7]) ram[rw_addr + 64'h7] <= wdata[63:56];
        end
    end  

    assign temp_rdata1 = ram[rw_addr];
    assign temp_rdata2 = ram[rw_addr + 64'h1];
    assign temp_rdata3 = ram[rw_addr + 64'h2];
    assign temp_rdata4 = ram[rw_addr + 64'h3];
    assign temp_rdata5 = ram[rw_addr + 64'h4];
    assign temp_rdata6 = ram[rw_addr + 64'h5];
    assign temp_rdata7 = ram[rw_addr + 64'h6];
    assign temp_rdata8 = ram[rw_addr + 64'h7];
    assign temp_rdata = {temp_rdata8,temp_rdata7,temp_rdata6,temp_rdata5,temp_rdata4,temp_rdata3,temp_rdata2,temp_rdata1};

    assign s_byte_rdata = {{56{temp_rdata[7]}},temp_rdata[7:0]};
    assign u_byte_rdata = {56'b0,temp_rdata[7:0]};
    assign byte_rdata = unsigned_read ? u_byte_rdata : s_byte_rdata;

    assign s_half_rdata = {{48{temp_rdata[15]}},temp_rdata[15:0]};
    assign u_half_rdata = {48'b0,temp_rdata[15:0]};
    assign half_rdata = unsigned_read ? u_half_rdata : s_half_rdata;

    assign s_word_rdata = {{32{temp_rdata[31]}},temp_rdata[31:0]};
    assign u_word_rdata = {32'b0,temp_rdata[31:0]};
    assign word_rdata = unsigned_read ? u_word_rdata : s_word_rdata;

    assign Read_data =  (RW_type[1:0] == `ysyx_20220337_RW_8bit) ? byte_rdata :
                        (RW_type[1:0] == `ysyx_20220337_RW_16bit) ? half_rdata :
                        (RW_type[1:0] == `ysyx_20220337_RW_32bit) ? word_rdata :
                        (RW_type[1:0] == `ysyx_20220337_RW_64bit) ? temp_rdata :
                        64'h0000_0000 ;

endmodule